package com.oracle.coherence.patterns.security;

import com.tangosol.net.DefaultCacheServer;
import com.tangosol.net.security.Security;

import javax.security.auth.Subject;
import javax.security.auth.callback.*;
import javax.security.auth.login.LoginContext;
import java.security.PrivilegedExceptionAction;

/**
 * This class runs an instance of <code>com.tangosol.net.DefaultCacheServer</code>
 * wrapped in a <code>java.security.PrivilegedExceptionAction</code> with a
 * specific <code>javax.security.auth.Subject</code>.
 * <p/>
 * This class will also redirect System.out and System.err to the Coherence log.
 * This can be overriden by setting the redirect.console system property to false.
 * The log level of the redirected system out will be 3, which equates to INFO. This
 * can be overriden with the redirect.console.level property which should be set to
 * a valid integer between 1 and 9 inclusive; 9 being the most verbose. If the
 * redirect.console.level system property is set to an invalid value then the
 * default of <code>CacheFactory.LOG_INFO</code>.
 * <p/>
 *
 * @author Jonathan Knight
 */
public class SecureCacheServer {

    private SecureCacheServer() {
    }

    private static void run(PrivilegedExceptionAction action) throws Exception {
        if (System.getProperties().containsKey(SystemPropertyLoader.MARKER_ENVIRONMENT)) {
            SystemPropertyLoader.loadProperties(null);
        }

        String moduleName = System.getProperty("coherence.security.loginmodule", "Coherence");
        String principal = System.getProperty("coherence.security.username");
        String password = System.getProperty("coherence.security.password");

        LoginContext lc = new LoginContext(moduleName, new SimpleCallbackHandler(principal, password));
        lc.login();
        Subject subject = lc.getSubject();
        Security.runAs(subject, action);
    }

    public static void start() throws Exception {
        run(new PrivilegedExceptionAction() {
            public Object run() throws Exception {
                DefaultCacheServer.start();
                return null;
            }
        });
    }

    public static void startDeamon() throws Exception {
        run(new PrivilegedExceptionAction() {
            public Object run() throws Exception {
                DefaultCacheServer.main(new String[0]);
                return null;
            }
        });
    }

    public static void main(final String[] args) throws Exception {
        SecureCacheServer.startDeamon();
    }

    public static class SimpleCallbackHandler implements CallbackHandler {
        private String username = null;
        private String password = null;

        public SimpleCallbackHandler(String pUsername, String pPassword) {
            username = pUsername;
            password = pPassword;
        }

        public void handle(Callback[] callbacks)
                throws java.io.IOException, UnsupportedCallbackException {
            for (Callback callback : callbacks) {
                if (callback instanceof NameCallback) {
                    NameCallback nc = (NameCallback) callback;
                    nc.setName(username);
                } else if (callback instanceof PasswordCallback) {
                    PasswordCallback pc = (PasswordCallback) callback;
                    pc.setPassword(password.toCharArray());
                } else {
                    throw new UnsupportedCallbackException(
                            callback, "Unrecognized Callback");
                }
            }
        }
    }
}
